﻿namespace SFWS.XJSON.Core
{
    using SFWS.XJSON.Core.Properties;
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.Data;
    using System.Globalization;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Web.Script.Serialization;

    public class JsonSerializer
    {
        private string dateFormat = null;
        private Type deleType = null;
        public static DateTime JS_MEDIANDATE = new DateTime(0x7b2, 1, 1, 0, 0, 0, DateTimeKind.Utc);
        private static JavaScriptSerializer jss = new JavaScriptSerializer();
        private static string nnod = string.Format(@"(?<nnode>(?<knode>{1})\s*:\s*{0})", vnod, nstr);
        private static string nnum = @"-?(0|[1-9]\d*)(\.\d+)?((e|E)(\+|-)?\d+)?";
        private static string nstr = "\"(\\\\(\"|\\\\|\\/|b|f|n|r|t|u[0-9a-fA-F]{4})|[^\"\\\\\\/\\b\\f\\n\\r\\t])*\"";
        private static string ntxt = "null|false|true";
        private static string rarr = string.Format(@"^\[\s*{0}?(\s*,\s*{0})*\s*\]$", vnod);
        private static string rnod = string.Format("^{0}$", nnod);
        private static string robj = string.Format(@"^\{{\s*{0}?(\s*,\s*{0})*\s*\}}$", nnod);
        private Func<object, string> typeFunc = null;
        private static string vnod = string.Format(@"(?<vnode>{0}|{1}|{2}|(?<arr>\[.*?\])|(?<obj>\{{.*?\}}))", ntxt, nnum, nstr);

        private void Append(StringBuilder sb, JsonObject val)
        {
            sb.Append("{");
            foreach (string str in val.Pairs.Keys)
            {
                this.Append(sb, str);
                sb.Append(":");
                this.AppendValue(sb, val.Pairs[str]);
                sb.Append(",");
            }
            if (val.Pairs.Count > 0)
            {
                sb.Length--;
            }
            sb.Append("}");
        }

        private void Append(StringBuilder sb, IEnumerable val)
        {
            if (val is NameValueCollection)
            {
                NameValueCollection values = val as NameValueCollection;
                sb.Append("{");
                foreach (string str in values.AllKeys)
                {
                    this.Append(sb, str);
                    sb.Append(":");
                    this.Append(sb, values[str]);
                    sb.Append(",");
                }
                if (values.Count > 0)
                {
                    sb.Length--;
                }
                sb.Append("}");
            }
            else
            {
                if (val is IDictionary)
                {
                    IDictionary dictionary = val as IDictionary;
                    bool flag = true;
                    StringBuilder builder = new StringBuilder();
                    builder.Append("{");
                    foreach (object obj2 in dictionary.Keys)
                    {
                        string str2 = this.Serialize(obj2, this.dateFormat);
                        if (!str2.StartsWith("\""))
                        {
                            flag = false;
                            break;
                        }
                        builder.Append(str2);
                        builder.Append(":");
                        this.AppendValue(builder, dictionary[obj2]);
                        builder.Append(",");
                    }
                    if (flag)
                    {
                        if (dictionary.Count > 0)
                        {
                            builder.Length--;
                        }
                        builder.Append("}");
                        sb.Append(builder.ToString());
                        return;
                    }
                }
                bool flag2 = false;
                sb.Append("[");
                foreach (object obj2 in val)
                {
                    this.AppendValue(sb, obj2);
                    sb.Append(",");
                    flag2 = true;
                }
                if (flag2)
                {
                    sb.Length--;
                }
                sb.Append("]");
            }
        }

        private void Append(StringBuilder sb, DataRow val)
        {
            sb.Append("{");
            foreach (DataColumn column in val.Table.Columns)
            {
                this.Append(sb, column.ColumnName);
                sb.Append(":");
                this.AppendValue(sb, val[column]);
                sb.Append(",");
            }
            if (val.Table.Columns.Count > 0)
            {
                sb.Length--;
            }
            sb.Append("}");
        }

        private void Append(StringBuilder sb, DataSet val)
        {
            sb.Append("{\"Tables\":[");
            foreach (DataTable table in val.Tables)
            {
                this.Append(sb, table);
                sb.Append(",");
            }
            if (val.Tables.Count > 0)
            {
                sb.Length--;
            }
            sb.Append("]}");
        }

        private void Append(StringBuilder sb, DataTable val)
        {
            sb.Append("{\"TableName\":");
            this.Append(sb, val.TableName);
            sb.Append(",\"Columns\":[");
            foreach (DataColumn column in val.Columns)
            {
                sb.Append("{\"ColumnName\":");
                this.Append(sb, column.ColumnName);
                sb.Append(",\"Caption\":");
                this.Append(sb, column.Caption);
                sb.Append("},");
            }
            if (val.Columns.Count > 0)
            {
                sb.Length--;
            }
            sb.Append("],\"Rows\":[");
            foreach (DataRow row in val.Rows)
            {
                this.Append(sb, row);
                sb.Append(",");
            }
            if (val.Rows.Count > 0)
            {
                sb.Length--;
            }
            sb.Append("]}");
        }

        private void Append(StringBuilder sb, DateTime val)
        {
            if (!string.IsNullOrEmpty(this.dateFormat))
            {
                this.Append(sb, val.ToString(this.dateFormat));
            }
            else
            {
                sb.Append("new Date(");
                sb.Append(val.ToUniversalTime().Subtract(JS_MEDIANDATE).TotalMilliseconds);
                sb.Append(")");
            }
        }

        private void Append(StringBuilder sb, string val)
        {
            sb.Append("\"");
            foreach (char ch in val)
            {
                switch (ch)
                {
                    case '\b':
                        sb.Append(@"\b");
                        break;

                    case '\t':
                        sb.Append(@"\t");
                        break;

                    case '\n':
                        sb.Append(@"\n");
                        break;

                    case '\f':
                        sb.Append(@"\f");
                        break;

                    case '\r':
                        sb.Append(@"\r");
                        break;

                    case '"':
                        sb.Append("\\\"");
                        break;

                    case '\\':
                        sb.Append(@"\\");
                        break;

                    default:
                    {
                        int num = ch;
                        if ((num < 0x20) || (num >= 0x7f))
                        {
                            sb.Append(@"\u").Append(num.ToString("X").PadLeft(4, '0'));
                        }
                        else
                        {
                            sb.Append(ch);
                        }
                        break;
                    }
                }
            }
            sb.Append("\"");
        }

        private void AppendObject(StringBuilder sb, object val)
        {
            List<MemberInfo> list = (from x in val.GetType().GetMembers(BindingFlags.Public | BindingFlags.Instance)
                where (x.MemberType == MemberTypes.Field) || (x.MemberType == MemberTypes.Property)
                select x).ToList<MemberInfo>();
            sb.Append("{");
            bool flag = false;
            foreach (MemberInfo info in list)
            {
                JsonIgnoreAttribute[] customAttributes;
                bool flag2 = false;
                object obj2 = null;
                if (info.MemberType == MemberTypes.Field)
                {
                    customAttributes = (JsonIgnoreAttribute[]) info.GetCustomAttributes(typeof(JsonIgnoreAttribute), false);
                    if ((customAttributes != null) && (customAttributes.Length > 0))
                    {
                        continue;
                    }
                    obj2 = ((FieldInfo) info).GetValue(val);
                    flag2 = true;
                }
                else
                {
                    customAttributes = (JsonIgnoreAttribute[]) info.GetCustomAttributes(typeof(JsonIgnoreAttribute), false);
                    if ((customAttributes != null) && (customAttributes.Length > 0))
                    {
                        continue;
                    }
                    PropertyInfo info2 = (PropertyInfo) info;
                    if (info2.CanRead && (info2.GetIndexParameters().Length == 0))
                    {
                        obj2 = info2.GetValue(val, null);
                        flag2 = true;
                    }
                }
                if (flag2)
                {
                    sb.AppendFormat("\"{0}\":", info.Name);
                    this.AppendValue(sb, obj2);
                    sb.Append(",");
                    flag = true;
                }
            }
            if (flag)
            {
                sb.Length--;
            }
            sb.Append("}");
        }

        private void AppendValue(StringBuilder sb, object val)
        {
            if ((val == null) || (val == DBNull.Value))
            {
                sb.Append("null");
            }
            else if (val.GetType() == this.deleType)
            {
                sb.Append(this.typeFunc(val));
            }
            else if (val is bool)
            {
                sb.Append(val.ToString().ToLower());
            }
            else if (val is char)
            {
                char ch = (char) val;
                if (ch == '\0')
                {
                    sb.Append("null");
                }
                else
                {
                    this.Append(sb, val.ToString());
                }
            }
            else if ((val is Guid) || (val is string))
            {
                this.Append(sb, val.ToString());
            }
            else if ((((((val is byte) || (val is short)) || ((val is int) || (val is long))) || (((val is sbyte) || (val is ushort)) || ((val is uint) || (val is ulong)))) || ((((val is byte?) || (val is short?)) || ((val is int?) || (val is long?))) || (((val is sbyte?) || (val is ushort?)) || (val is uint?)))) || (val is ulong?))
            {
                sb.Append(val);
            }
            else if ((val is float) || (val is float?))
            {
                sb.Append(((float) val).ToString(CultureInfo.InvariantCulture.NumberFormat));
            }
            else if ((val is double) || (val is double?))
            {
                sb.Append(((double) val).ToString(CultureInfo.InvariantCulture.NumberFormat));
            }
            else if ((val is decimal) || (val is decimal?))
            {
                sb.Append(((decimal) val).ToString(CultureInfo.InvariantCulture.NumberFormat));
            }
            else if (val.GetType().IsEnum)
            {
                sb.Append((int) val);
            }
            else if (val is TimeSpan)
            {
                TimeSpan span = (TimeSpan) val;
                sb.Append(span.Ticks);
            }
            else if (val is DateTime)
            {
                this.Append(sb, (DateTime) val);
            }
            else if (val is DataSet)
            {
                this.Append(sb, val as DataSet);
            }
            else if (val is DataTable)
            {
                this.Append(sb, val as DataTable);
            }
            else if (val is DataRow)
            {
                this.Append(sb, val as DataRow);
            }
            else if (val is Uri)
            {
                this.Append(sb, (val as Uri).GetComponents(UriComponents.AbsoluteUri, UriFormat.UriEscaped));
            }
            else if (val is JsonObject)
            {
                this.Append(sb, val as JsonObject);
            }
            else if (val is IEnumerable)
            {
                this.Append(sb, val as IEnumerable);
            }
            else
            {
                this.AppendObject(sb, val);
            }
        }

        public T Deserialize<T>(string json)
        {
            object obj2 = this.Deserialize(json, typeof(T));
            if (obj2 == null)
            {
                return default(T);
            }
            return (T) obj2;
        }

        public T Deserialize<T>(string json, string dateFormat)
        {
            object obj2 = this.Deserialize(json, typeof(T), dateFormat);
            if (obj2 == null)
            {
                return default(T);
            }
            return (T) obj2;
        }

        public object Deserialize(string json, Type targetType)
        {
            return this.Deserialize(json, targetType, this.DateFormat);
        }

        public object Deserialize(string json, Type targetType, string dateFormat)
        {
            object obj2;
            this.dateFormat = dateFormat;
            if (string.IsNullOrEmpty(json))
            {
                throw new ArgumentNullException("json", Resources.JsonException);
            }
            json = json.Trim();
            if (json == "null")
            {
                if (targetType.IsValueType && (!targetType.IsGenericType || !(targetType.GetGenericTypeDefinition() == typeof(Nullable<>))))
                {
                    throw new InvalidOperationException(Resources.DeserializeException);
                }
                return null;
            }
            if ((json == "true") || (json == "false"))
            {
                if (((targetType != typeof(bool)) && (targetType != typeof(bool?))) && !(targetType == typeof(object)))
                {
                    throw new InvalidOperationException(Resources.DeserializeException);
                }
                return (json == "true");
            }
            if ((targetType == typeof(DateTime)) || (targetType == typeof(DateTime?)))
            {
                return this.DeserializeDate(json);
            }
            if (json.StartsWith("\"") || json.EndsWith("\""))
            {
                if (!(json.StartsWith("\"") && json.EndsWith("\"")))
                {
                    throw new ArgumentException(Resources.JsonException, "json");
                }
                if (targetType == typeof(char))
                {
                    json = jss.Deserialize<string>(json);
                    if (json.Length != 1)
                    {
                        throw new InvalidOperationException(Resources.DeserializeException);
                    }
                    return json[0];
                }
                if (targetType == typeof(Guid))
                {
                    json = jss.Deserialize<string>(json);
                    return new Guid(json);
                }
                if (targetType == typeof(Uri))
                {
                    json = jss.Deserialize<string>(json);
                    return new Uri(json);
                }
                if ((targetType != typeof(string)) && (targetType != typeof(object)))
                {
                    throw new InvalidOperationException(Resources.DeserializeException);
                }
                return jss.Deserialize<string>(json);
            }
            if (json.StartsWith("[") || json.EndsWith("]"))
            {
                if (!json.StartsWith("[") || !json.EndsWith("]"))
                {
                    throw new ArgumentException(Resources.JsonException, "json");
                }
                return this.DeserializeArray(json, targetType);
            }
            if (json.StartsWith("{") || json.EndsWith("}"))
            {
                if (!json.StartsWith("{") || !json.EndsWith("}"))
                {
                    throw new ArgumentException(Resources.JsonException, "json");
                }
                return this.DeserializeObject(json, targetType);
            }
            if ((targetType == typeof(byte)) || (targetType == typeof(byte?)))
            {
                return byte.Parse(json);
            }
            if ((targetType == typeof(short)) || (targetType == typeof(short?)))
            {
                return short.Parse(json);
            }
            if ((targetType == typeof(int)) || (targetType == typeof(int?)))
            {
                return int.Parse(json);
            }
            if ((targetType == typeof(long)) || (targetType == typeof(long?)))
            {
                return long.Parse(json);
            }
            if ((targetType == typeof(sbyte)) || (targetType == typeof(sbyte?)))
            {
                return sbyte.Parse(json);
            }
            if ((targetType == typeof(ushort)) || (targetType == typeof(ushort?)))
            {
                return ushort.Parse(json);
            }
            if ((targetType == typeof(uint)) || (targetType == typeof(uint?)))
            {
                return uint.Parse(json);
            }
            if ((targetType == typeof(ulong)) || (targetType == typeof(ulong?)))
            {
                return ulong.Parse(json);
            }
            if ((targetType == typeof(float)) || (targetType == typeof(float?)))
            {
                return float.Parse(json, CultureInfo.InvariantCulture.NumberFormat);
            }
            if ((targetType == typeof(double)) || (targetType == typeof(double?)))
            {
                return double.Parse(json, CultureInfo.InvariantCulture.NumberFormat);
            }
            if ((targetType == typeof(decimal)) || (targetType == typeof(decimal?)))
            {
                return decimal.Parse(json, CultureInfo.InvariantCulture.NumberFormat);
            }
            if (targetType.IsEnum)
            {
                return Enum.ToObject(targetType, int.Parse(json));
            }
            if (targetType == typeof(TimeSpan))
            {
                return new TimeSpan(long.Parse(json));
            }
            try
            {
                obj2 = jss.DeserializeObject(json);
            }
            catch (Exception exception)
            {
                throw new ArgumentException(Resources.JsonException, "json", exception);
            }
            return obj2;
        }

        private object DeserializeArray(string json, Type targetType)
        {
            Type type;
            Array array;
            int num;
            List<string> list = new JsonParser().ParseArray(json.Substring(1, json.Length - 2));
            if (targetType.IsArray)
            {
                type = targetType.GetElementType();
                if (list.Count < 1)
                {
                    return Array.CreateInstance(type, 0);
                }
                array = Array.CreateInstance(type, list.Count);
                for (num = 0; num < list.Count; num++)
                {
                    array.SetValue(this.Deserialize(list[num], type, this.dateFormat), num);
                }
                return array;
            }
            if (targetType.GetInterfaces().Contains<Type>(typeof(IList)))
            {
                IList source = Activator.CreateInstance(targetType) as IList;
                if (list.Count >= 1)
                {
                    type = null;
                    try
                    {
                        type = source.AsQueryable().ElementType;
                    }
                    catch
                    {
                        type = typeof(object);
                    }
                    for (num = 0; num < list.Count; num++)
                    {
                        source.Add(this.Deserialize(list[num], type, this.dateFormat));
                    }
                }
                return source;
            }
            if (!(targetType == typeof(object)))
            {
                throw new InvalidOperationException(Resources.DeserializeException);
            }
            Type elementType = typeof(object[]);
            array = Array.CreateInstance(elementType, list.Count);
            for (num = 0; num < list.Count; num++)
            {
                array.SetValue(this.Deserialize(list[num], targetType, this.dateFormat), num);
            }
            return array;
        }

        private DateTime DeserializeDate(string json)
        {
            DateTime time;
            try
            {
                if (!string.IsNullOrEmpty(this.dateFormat))
                {
                    json = jss.Deserialize<string>(json);
                    return DateTime.ParseExact(json, this.dateFormat, DateTimeFormatInfo.InvariantInfo);
                }
                Match match = Regex.Match(json, @"^new\s+Date\s*\(\s*(\+|-)?\s*(\d+\.?\d*)\s*\)$");
                time = JS_MEDIANDATE.AddMilliseconds(double.Parse(match.Groups[1].Value + match.Groups[2].Value));
            }
            catch
            {
                throw new InvalidOperationException(Resources.DeserializeException);
            }
            return time;
        }

        private object DeserializeObject(string json, Type targetType)
        {
            Dictionary<string, string> dictionary = new JsonParser().ParseObject(json.Substring(1, json.Length - 2));
            if (dictionary.Count < 1)
            {
                return Activator.CreateInstance(targetType);
            }
            if (targetType == typeof(JsonObject))
            {
                JsonObject obj2 = new JsonObject();
                foreach (string str in dictionary.Keys)
                {
                    obj2.Add(str, this.Deserialize(dictionary[str], typeof(object), this.dateFormat));
                }
                return obj2;
            }
            if (targetType == typeof(NameValueCollection))
            {
                NameValueCollection values = new NameValueCollection();
                foreach (string str in dictionary.Keys)
                {
                    values.Add(str, dictionary[str]);
                }
                return values;
            }
            if (targetType.GetInterfaces().Contains<Type>(typeof(IDictionary)))
            {
                IDictionary dictionary2 = Activator.CreateInstance(targetType) as IDictionary;
                foreach (string str in dictionary.Keys)
                {
                    dictionary2.Add(str, this.Deserialize(dictionary[str], typeof(object), this.dateFormat));
                }
                return dictionary2;
            }
            object obj3 = Activator.CreateInstance(targetType);
            Dictionary<string, MemberInfo> dictionary3 = Enumerable.ToDictionary<MemberInfo, string>(from y in targetType.GetMembers(BindingFlags.Public | BindingFlags.Instance)
                where (y.MemberType == MemberTypes.Field) || (y.MemberType == MemberTypes.Property)
                select y, (Func<MemberInfo, string>) (x => x.Name));
            foreach (string str2 in dictionary.Keys)
            {
                object obj4;
                if (!dictionary3.Keys.Contains<string>(str2))
                {
                    throw new InvalidOperationException(Resources.DeserializeException);
                }
                if (dictionary3[str2].MemberType == MemberTypes.Field)
                {
                    FieldInfo info = (FieldInfo) dictionary3[str2];
                    obj4 = this.Deserialize(dictionary[str2], info.FieldType, this.dateFormat);
                    info.SetValue(obj3, obj4);
                }
                else
                {
                    PropertyInfo info2 = (PropertyInfo) dictionary3[str2];
                    obj4 = this.Deserialize(dictionary[str2], info2.PropertyType, this.dateFormat);
                    if (!info2.CanWrite || (info2.GetIndexParameters().Length != 0))
                    {
                        throw new InvalidOperationException(Resources.DeserializeException);
                    }
                    info2.SetValue(obj3, obj4, null);
                }
            }
            return obj3;
        }

        public string Serialize(object val)
        {
            return this.Serialize(val, this.DateFormat);
        }

        public string Serialize(object val, string dateFormat)
        {
            this.dateFormat = dateFormat;
            StringBuilder sb = new StringBuilder();
            this.AppendValue(sb, val);
            return sb.ToString();
        }

        public string Serialize(object val, Type type, Func<object, string> f)
        {
            this.typeFunc = f;
            this.deleType = type;
            StringBuilder sb = new StringBuilder();
            this.AppendValue(sb, val);
            return sb.ToString();
        }

        public string DateFormat { get; set; }
    }
}

